DepositData.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. "use client";
  2. import { DepositsTypes, RewardsType } from "@/api/depositsApi";
  3. import { getUserMoneyApi, getUserRechargeApi } from "@/api/user";
  4. import Box from "@/components/Box";
  5. import ButtonOwn from "@/components/ButtonOwn";
  6. import TipsModal, { ModalProps } from "@/components/TipsModal";
  7. import { useUserInfoStore } from "@/stores/useUserInfoStore";
  8. import { neReg } from "@/utils";
  9. import { Button, Form, Input, Toast } from "antd-mobile";
  10. import { FormInstance } from "antd-mobile/es/components/form";
  11. import { useTranslations } from "next-intl";
  12. import { FC, useLayoutEffect, useRef, useState } from "react";
  13. import actions from "@/app/[locale]/(TabBar)/deposit/actions";
  14. import Empty from "@/components/Empty";
  15. import { useEventPoint } from "@/hooks/useEventPoint";
  16. import { useRouter } from "@/i18n/routing";
  17. import { useSystemStore } from "@/stores/useSystemStore";
  18. import "@/styles/deposit.scss";
  19. import { server } from "@/utils/client";
  20. import clsx from "clsx";
  21. import { Swiper, SwiperSlide } from "swiper/react";
  22. interface Props {}
  23. interface RewardsProps {
  24. rewards: RewardsType[];
  25. }
  26. const RewardsText: FC<RewardsProps> = (props) => {
  27. const { rewards } = props;
  28. // {/*1现金2彩金3免费币4重玩币*/}
  29. const t = useTranslations();
  30. const calcRewards = rewards
  31. .map((item) => {
  32. // let text = item.ratio > 0 ? `${item.ratio}%` : `${item.reward}`;
  33. // 根据coin_type添加后缀
  34. const suffixes = {
  35. 1: t("ProfilePage.balance"),
  36. 2: t("ProfilePage.bonus"),
  37. 3: t("ProfilePage.free"),
  38. 4: t("ProfilePage.replay"),
  39. };
  40. let text = "";
  41. // 设置基础奖励文本
  42. if (item.ratio > 0) {
  43. text = `${item.ratio}%`;
  44. } else {
  45. text = `${item.reward}`;
  46. }
  47. if (item.ratio > 0 && item.reward > 0) {
  48. text = `${item.ratio}%${suffixes[item.coin_type]}, ${item.reward}`;
  49. }
  50. // 如果coin_type在suffixes中存在,则添加相应的后缀
  51. if (suffixes.hasOwnProperty(item.coin_type)) {
  52. text += suffixes[item.coin_type];
  53. }
  54. return text;
  55. })
  56. .join(",");
  57. return (
  58. <div className={"flex flex-1 flex-wrap break-all"}>
  59. <span className="amountTips">+ {calcRewards} </span>
  60. </div>
  61. );
  62. };
  63. const getDepositApi = async () => {
  64. return server
  65. .request<DepositsTypes[]>({
  66. url: "/v1/api/user/user_deposit_config",
  67. method: "post",
  68. data: { activity_type: 0 },
  69. })
  70. .then((res) => {
  71. if (res.code === 200) return res.data;
  72. return [];
  73. });
  74. };
  75. const DepositData: FC<Props> = (props) => {
  76. const t = useTranslations();
  77. const router = useRouter();
  78. const userInfo = useUserInfoStore((state) => state.userInfo);
  79. const { eventInitiate } = useEventPoint();
  80. const isStrictMode = useSystemStore((state) => state.identity_verify.deposit === 1);
  81. const [depositState, setDepositState] = useState<DepositsTypes[]>([]);
  82. const [activeType, setActiveType] = useState<Partial<DepositsTypes>>({});
  83. const formInstanceRef = useRef<FormInstance>(null);
  84. let [amount, setAmount] = useState<number | undefined>(undefined);
  85. const titleChangeHandler = (item: DepositsTypes, index: number) => {
  86. setAmount(undefined);
  87. setActiveType(item);
  88. formInstanceRef.current?.resetFields();
  89. };
  90. const tipModelRef = useRef<ModalProps>(null); // 充值清空打码量弹窗
  91. const [formData, setFormData] = useState<any>({}); // 存放表单数据
  92. const onFinish = async (values: any) => {
  93. const params = { ...values, channel_id: activeType.id, amount: +values.amount };
  94. const res = await getUserMoneyApi();
  95. if (res.data?.tips_reset_rollover) {
  96. tipModelRef.current?.onOpen();
  97. setFormData(params);
  98. return;
  99. }
  100. handleUserRecharge(false, params);
  101. };
  102. const handleUserRecharge = (is_reset_rollover: boolean, data?: any) => {
  103. let params = data || formData;
  104. getUserRechargeApi({ is_reset_rollover, ...params })
  105. .then(async (res) => {
  106. formInstanceRef.current?.resetFields();
  107. // Toast.show({ icon: "success", content: t("code.200"), maskClickable: false });
  108. tipModelRef.current?.onClose();
  109. setAmount(undefined);
  110. // const url =
  111. // "https://caixa.pay4z.com/brl/qrcode.html?tradeNo=T2501180056ijk21kJ&amount=20&payAmount=20&currency=BRL&expiredAt=2025-01-18%2001%3A11%3A31&expire=1737173491282&raw=00020101021226870014br.gov.bcb.pix2565qrcode.santsbank.com%2Fdynamic%2Fdc8cf003-1616-47f8-94e6-16be500d05b45204000053039865802BR5907LF%20LTDA6009Sao%20Paulo62070503***6304DF54&type=QRCODE";
  112. if (res.data.pay_url) {
  113. eventInitiate();
  114. // fix: ios 限制
  115. setTimeout(() => {
  116. window.open(res.data.pay_url);
  117. }, 0);
  118. } else {
  119. Toast.show({ icon: "success", content: t("code.200"), maskClickable: false });
  120. }
  121. const data = await getDepositApi();
  122. setDepositState(data);
  123. setActiveType(data[0]);
  124. await actions();
  125. })
  126. .catch((error) => {
  127. Toast.show({ content: t(`code.${error.data.code}`), maskClickable: false });
  128. });
  129. };
  130. const onValuesChange = (changeValues: any) => {
  131. if (changeValues.amount) {
  132. setAmount(changeValues.amount);
  133. }
  134. };
  135. const amountChange = (value: number) => {
  136. formInstanceRef.current?.setFieldValue("amount", value);
  137. setAmount(value);
  138. };
  139. const amountValidator = (rules: any, value: any) => {
  140. if (!value) return Promise.reject(new Error(t("form.amount")));
  141. if (+value < activeType.min_amount!)
  142. return Promise.reject(
  143. new Error(t("form.amountMinReg", { amount: activeType.min_amount }))
  144. );
  145. if (+value > activeType.max_amount!)
  146. return Promise.reject(
  147. new Error(t("form.amountMaxReg", { amount: activeType.max_amount }))
  148. );
  149. return Promise.resolve();
  150. };
  151. useLayoutEffect(() => {
  152. getDepositApi().then((data) => {
  153. setDepositState(data);
  154. setActiveType(data[0]);
  155. });
  156. }, []);
  157. if (!depositState.length) return <Empty />;
  158. return (
  159. <div className="deposit-box">
  160. <div className="img-box">
  161. <img
  162. className={"h-[100%] w-[100%] object-cover"}
  163. src={activeType.icon || ""}
  164. alt={activeType.name || ""}
  165. width={160}
  166. height={40}
  167. />
  168. </div>
  169. {/*<div className={"flex flex-wrap"}>*/}
  170. {/* {depositState.map((item, index) => {*/}
  171. {/* return (*/}
  172. {/* <Fragment key={item.id}>*/}
  173. {/* <p*/}
  174. {/* className="btn-box truncate"*/}
  175. {/* style={{*/}
  176. {/* borderColor: activeType?.id === item.id ? "#ff9323" : "#333",*/}
  177. {/* }}*/}
  178. {/* onClick={() => titleChangeHandler(item, index)}*/}
  179. {/* >*/}
  180. {/* {item.name}*/}
  181. {/* </p>*/}
  182. {/* </Fragment>*/}
  183. {/* );*/}
  184. {/* })}*/}
  185. {/*</div>*/}
  186. {/* {
  187. background: activeType?.id === item.id ? "#d917ff" : "transparent",
  188. boxShadow: activeType?.id === item.id ? "none":,
  189. borderColor: activeType?.id === item.id ? "#d917ff",
  190. } */}
  191. <Swiper slidesPerView={"auto"} centeredSlidesBounds>
  192. {depositState?.map((item, index) => (
  193. <SwiperSlide key={index} className={"max-w-fit"}>
  194. <p
  195. className={clsx("btn-box truncate", {
  196. active: activeType?.id === item.id, // 检查是否为当前活动项,添加active类名
  197. })}
  198. onClick={() => titleChangeHandler(item, index)}
  199. >
  200. {item.name}
  201. </p>
  202. </SwiperSlide>
  203. ))}
  204. </Swiper>
  205. <Box className={"custom-form"} style={{ padding: 0 }}>
  206. <Form
  207. style={{
  208. "--border-bottom": "none",
  209. "--border-top": "none",
  210. "--border-inner": "none",
  211. }}
  212. ref={formInstanceRef}
  213. footer={
  214. <>
  215. <ButtonOwn active>{t("DepositPage.DepositarAgora")}</ButtonOwn>
  216. </>
  217. }
  218. initialValues={userInfo}
  219. onFinish={onFinish}
  220. onValuesChange={onValuesChange}
  221. >
  222. {isStrictMode ? (
  223. <>
  224. <Form.Item
  225. name="user_name"
  226. label=""
  227. rules={[{ required: true, message: t("form.usernameReg") }]}
  228. >
  229. <Input placeholder={t("form.username")} />
  230. </Form.Item>
  231. <Form.Item
  232. name="passport"
  233. label=""
  234. rules={[
  235. { required: true, message: t("form.cardReg"), pattern: neReg },
  236. ]}
  237. >
  238. <Input placeholder={t("form.card")} maxLength={11} type={"text"} />
  239. </Form.Item>
  240. </>
  241. ) : null}
  242. <Form.Item
  243. name="amount"
  244. label=""
  245. rules={[{ required: true, type: "number", validator: amountValidator }]}
  246. >
  247. <Input
  248. placeholder={`${t("DepositPage.Montante")}: (${activeType?.min_amount}-${activeType?.max_amount})`}
  249. type={"number"}
  250. maxLength={activeType?.max_amount}
  251. />
  252. </Form.Item>
  253. <div className={"flex flex-col"}>
  254. <div className={"flex-1"}>
  255. <ul className="ul-box">
  256. {activeType?.products?.map((item, index) => (
  257. <li
  258. className={amount == item.amount ? "active" : ""}
  259. key={index}
  260. onClick={() => amountChange(item.amount)}
  261. >
  262. {!!item.badge && <span className="hot"></span>}
  263. <div className="amountContent">
  264. {/* <span className="iconfont icon-unit-brl"></span> */}
  265. <span className="iconfont">R$</span>
  266. <span> {item.amount}</span>
  267. </div>
  268. {item.rewards && (
  269. <RewardsText
  270. rewards={
  271. item.rewards &&
  272. item.rewards.sort(
  273. (p, n) => p.coin_type - n.coin_type
  274. )
  275. }
  276. />
  277. )}
  278. </li>
  279. ))}
  280. </ul>
  281. </div>
  282. </div>
  283. </Form>
  284. <div className={"mt-[5px] text-[0.12rem] text-[#3af3ff]"}>
  285. {t("DepositPage.depositTips")}
  286. </div>
  287. </Box>
  288. <TipsModal title={"Tips"} ref={tipModelRef}>
  289. <div className={"flex items-center justify-between"}>
  290. <h2 className={"text-[0.14rem]"}>Mantenha Bônus</h2>
  291. <Button
  292. color={"primary"}
  293. className={"mx-auto"}
  294. style={{
  295. "--background-color": "var(--primary-color)",
  296. "--border-color": "var(--primary-color)",
  297. }}
  298. onClick={() => handleUserRecharge(false)}
  299. >
  300. Depósito
  301. </Button>
  302. </div>
  303. <p className={"mt-[0.05rem] text-left text-[0.12rem] font-medium text-[#666]"}>
  304. Recarga direta, mantendo a carteira principal e informações de carteira de
  305. bônus;
  306. </p>
  307. <b
  308. className={"mb-[0.2rem] mt-[0.2rem]"}
  309. style={{
  310. width: "100%",
  311. height: "auto",
  312. borderTop: "1px solid #e5e5e5",
  313. display: "block",
  314. }}
  315. ></b>
  316. <div className={"flex items-center justify-between"}>
  317. <h2 className={"text-[0.14rem]"}>Esvaziem Bônus</h2>
  318. <Button
  319. color={"primary"}
  320. className={"mx-auto"}
  321. style={{
  322. "--background-color": "var(--primary-color)",
  323. "--border-color": "var(--primary-color)",
  324. }}
  325. onClick={() => handleUserRecharge(true)}
  326. >
  327. Depósito
  328. </Button>
  329. </div>
  330. <p className={"mt-[0.05rem] text-left text-[0.12rem] font-medium text-[#64d23e]"}>
  331. Após a recarga, o saldo principal é mantido e todo o bônus é esvaziado, a nova
  332. carteira principal e a carteira de bônus são re-gravadas a quantidade de códigos
  333. jogados;
  334. </p>
  335. </TipsModal>
  336. </div>
  337. );
  338. };
  339. export default DepositData;